package com.bingxue.edu.management.shop.service;

import cn.hutool.http.HttpRequest;
import cn.hutool.http.HttpResponse;
import cn.hutool.json.JSONObject;
import cn.hutool.json.JSONUtil;
import com.bingxue.edu.common.config.FuiouPayProperties;
import com.bingxue.edu.common.util.FuiouPaySignUtil;
import com.bingxue.edu.management.shop.model.query.PayResultQuery;
import com.bingxue.edu.management.shop.model.req.FuiouPayOrderCreateReq;
import com.bingxue.edu.management.shop.model.req.FuiouRefundOrderCreateReq;
import com.bingxue.edu.framework.common.util.CheckUtils;
import lombok.AllArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Service;

import java.math.BigDecimal;
import java.math.RoundingMode;
import java.util.HashMap;
import java.util.Map;

import static cn.hutool.core.date.DatePattern.PURE_DATE_FORMATTER;

/**
 * 富友支付
 * @author panbo
 * @since 2024/9/7
 */
@Slf4j
@Service
@AllArgsConstructor
public class FuiouPayService {

    private final FuiouPayProperties fuiouPayProperties;

    /**
     * 支付接口
     * <a href="http://180.168.100.158:13318/fuiouWposApipay/jie-kou-lie-biao/ding-dan-zhi-fu-jie-kou.html">...</a>
     */
    public JSONObject createPayOrder(FuiouPayOrderCreateReq req) {
        Map<String, String> message = new HashMap<>();
        // 商户在富友入网时生成的商户号
        message.put("mchnt_cd", fuiouPayProperties.getMchntCd());
        // 订单日期 由对接商户系统产生，格式：YYYYMMDD
        message.put("order_date", req.getOrderDate().format(PURE_DATE_FORMATTER));
        // 订单号 由商户端生成，条件：唯一，数字和英文组合
        message.put("order_id", req.getOrderId());
        // 订单金额 单位为分
        message.put("order_amt", String.valueOf(convertCentAmount(req.getOrderAmount())));
        // 支付方式 二维码链接支付[ALIPAY-支付宝,WECHAT-微信]；公众号小程序支付[JSAPI-公众号支付,FWC-支付宝小程序,LETPAY-微信小程序]
        message.put("order_pay_type", "LETPAY");
        // 通知地址 订单支付成功，富友会根据此地址通知对接商户系统(POST)
        message.put("back_notify_url", req.getBackNotifyUrl() != null ? req.getBackNotifyUrl() : fuiouPayProperties.getBackNotifyUrl());
        // 商品名称-大约15个中文
        message.put("goods_name", req.getGoodsName());
        // 商品描述
        message.put("goods_detail", req.getGoodsDetail() == null ? req.getGoodsName() : req.getGoodsDetail());
        // 微信app id
        message.put("appid", fuiouPayProperties.getWxAppId());
        // 公众号或小程序用户OPENID
        message.put("openid", req.getOpenid());
        // 超时时间按分钟计算，1-360，不传默认120分钟
        message.put("order_timeout", fuiouPayProperties.getOrderTimeout().toString());
        // 版本号
        message.put("ver", "1.0.1");

        Map<String, String> signedParams = signRequest(message);
        HttpResponse httpResponse = HttpRequest
//                .post("https://hlwnets-test.fuioupay.com/aggpos/order.fuiou")
                .post("https://hlwnets.fuioupay.com/aggpos/order.fuiou")
                .body(JSONUtil.toJsonStr(signedParams))
                .execute();
        return parseHttpResponse(httpResponse);
    }

    /**
     * 查询支付结果
     * <a href="http://180.168.100.158:13318/fuiouWposApipay/jie-kou-lie-biao/ding-dan-cha-xun-jie-kou.html">...</a>
     */
    public JSONObject queryPayResult(PayResultQuery query) {
        Map<String, String> message = new HashMap<>();
        // 商户在富友入网时生成的商户号
        message.put("mchnt_cd", fuiouPayProperties.getMchntCd());
        // 订单日期 由对接商户系统产生，格式：YYYYMMDD
        message.put("order_date", query.getOrderDate().format(PURE_DATE_FORMATTER));
        // 订单号 由商户端生成，条件：唯一，数字和英文组合
        message.put("order_id", query.getOrderId());
        // 版本号
        message.put("ver", "1.0.0");

        Map<String, String> signedParams = signRequest(message);
        HttpResponse httpResponse = HttpRequest
                .post("https://hlwnets.fuioupay.com/aggpos/orderQuery.fuiou")
                .body(JSONUtil.toJsonStr(signedParams))
                .execute();
        return parseHttpResponse(httpResponse);
    }

    public JSONObject createRefundOrder(FuiouRefundOrderCreateReq req) {
        Map<String, String> message = new HashMap<>();
        // 商户在富友入网时生成的商户号
        message.put("mchnt_cd", fuiouPayProperties.getMchntCd());
        // 退款日期 退款交易发起的日期，格式：YYYYMMDD
        message.put("refund_order_date", req.getRefundOrderDate().format(PURE_DATE_FORMATTER));
        // 退款交易流水号，商户生成。唯一。
        message.put("refund_order_id", req.getRefundOrderId());
        // 订单日期 由对接商户系统产生，格式：YYYYMMDD
        message.put("pay_order_date", req.getPayOrderDate().format(PURE_DATE_FORMATTER));
        // 订单号 由商户端生成，条件：唯一，数字和英文组合
        message.put("pay_order_id", req.getPayOrderId());
        // 退款金额 单位为分
        message.put("refund_amt", String.valueOf(convertCentAmount(req.getRefundAmount())));
        // 版本号
        message.put("ver", "1.0.0");

        Map<String, String> signedParams = signRequest(message);
        HttpResponse httpResponse = HttpRequest
//                .post("https://refund-transfer-test.fuioupay.com/refund_transfer/aggposRefund.fuiou")
                .post("https://refund-transfer.fuioupay.com/refund_transfer/aggposRefund.fuiou")
                .body(JSONUtil.toJsonStr(signedParams))
                .execute();
        return parseHttpResponse(httpResponse);
    }

    public Map<String, String> signRequest(Map<String, String> message) {
        Map<String, String> request = new HashMap<>();
        request.put("mchnt_cd", message.get("mchnt_cd"));
        String signedMessageStr = FuiouPaySignUtil.encryptByRsaPub(JSONUtil.toJsonStr(message), fuiouPayProperties.getFuiouPublicKey(), "GBK");
        request.put("message", signedMessageStr);
        return request;
    }

    public JSONObject parseHttpResponse(HttpResponse httpResponse) {
        int status = httpResponse.getStatus();
        String body = httpResponse.body();
        CheckUtils.throwIfNotEqual(status, 200, "请求富友支付出错, status: {}, body: {} ", status, body);
        JSONObject result = new JSONObject(body);
        CheckUtils.throwIfNotEqual(result.get("resp_code"), "0000", "请求富友支付出错, status: {}, body: {} ", status, body);
        String message = FuiouPaySignUtil.decryptByRsaPri(result.getStr("message"), fuiouPayProperties.getMchntPrivateKey(), "GBK");
        return new JSONObject(message);
    }


    private static int convertCentAmount(BigDecimal amount) {
        return amount.multiply(BigDecimal.valueOf(100)).setScale(0, RoundingMode.HALF_UP).intValue();
    }
}
